/*
 * net_s.h
 *
 *  Created on: Sep 26, 2013
 *      Author: killerg
 */

#ifndef NET_S_H_
#define NET_S_H_

#include <atomic>
#include <sys/epoll.h>
#include <functional>
#include <map>
#include <queue>
#include <string.h>

#include "net_buffer.h"
#include "msg_coder.h"

struct tran_s;
struct evbuffer;
struct epoll_event;

struct net_s {
    typedef std::function<void(tran_s*)> handler_t;

    net_s() {
        epfd = -1;
        max_fdnum = 0;
        cur_fdnum= 0;
        accept_fd = -1;
        accept_tr = NULL;
        events = NULL;
        bytes_send = 0;
        bytes_recv = 0;
        valid_serial = 0;
        code_cb = NULL;
    }

    bool init(int max_client, code_cb_t code_cb = NULL, size_t max_msg_size = 0xffffffff, bool nodelay = true, int deal_per_tick = 40);
    void destroy();

    void update();

    bool start_accept(const char* ip, int port,
            handler_t logoncb,
            handler_t logoffcb,
            handler_t acceptcb = NULL);
    void stop_accept();

    int epfd;
    int max_fdnum;
    int cur_fdnum;
    int accept_fd;
    tran_s* accept_tr;
    epoll_event* events;
    std::queue<tran_s*> gc_queue;
    std::queue<tran_s*> accept_queue;
    handler_t logoncb;
    handler_t logoffcb;
    handler_t acceptcb;
    code_cb_t code_cb;
    size_t max_msg_size;
    bool nodelay;
    int deal_per_tick;

    int32_t bytes_send;
    int32_t bytes_recv;
    int64_t valid_serial;
};


struct tran_s {
    const static int MAX_MSG_INDEX = 1000000;
    const static int INIT_MSG_INDEX = 0;

    template<typename MsgT>
    bool send(MsgT* &msg, size_t* sended_size = NULL) {
        int before = get_output_len();
        uint32_t msg_size = 0;
        bool ok = evbuffer_send<MsgT>(output, msg, &msg_size);
        if (ok) {
            bytes_send += msg_size;
            deal_epoll(EPOLL_CTL_MOD, EPOLLIN|EPOLLOUT/*|EPOLLET*/);
            if (sended_size)
                *sended_size = msg_size;
        }
        return ok;
    }

    bool send_buffer(const void* data, size_t len) {
        int before = get_output_len();
        if (evbuffer_add(output, data, len) == 0) {
            bytes_send += len;
            deal_epoll(EPOLL_CTL_MOD, EPOLLIN|EPOLLOUT/*|EPOLLET*/);
            return true;
        }
        return false;
    }

    size_t write_line(const char* buffer) {
        int before = get_output_len();
        uint32_t msg_size = 0;
        msg_size = evbuffer_add_printf(output, "%s\n", buffer);
        if (msg_size > 0) {
            bytes_send += msg_size;
            deal_epoll(EPOLL_CTL_MOD, EPOLLIN|EPOLLOUT/*|EPOLLET*/);
        }
        return msg_size;
    }

    size_t read_line(char* buffer) {

        size_t n_read_out;
        char* buff = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF);
        if (buff) {
            bytes_recv += n_read_out;

            strncpy(buffer, buff, n_read_out+1);
            free(buff);
        }

        return n_read_out;
    }


    template<typename MsgT>
    MsgT* recv(size_t* msg_size = NULL) {
        uint32_t frame_size = 0;
        MsgT* msg = evbuffer_recv<MsgT>(input, &frame_size, recv_index, ns->code_cb);
        if (frame_size >= ns->max_msg_size) {
            if (msg) {
                delete msg;
                msg = NULL;
            }
            disconnect(true);
        }

        if (NULL != msg) {
            bytes_recv += frame_size;
            if (msg_size)
                *msg_size = frame_size - sizeof(uint32_t);

            if (++recv_index > MAX_MSG_INDEX)
                recv_index = INIT_MSG_INDEX;
        }
        return msg;
    }

    tran_s(int fd, net_s* ns);
    ~tran_s();
    void disconnect(bool need_logoff = true);

    bool deal_epoll(int ctrl, int events);
    int get_output_len();

    bool need_logoff;
    bool closed;
    int fd;
    net_s* ns;
    evbuffer* input;
    evbuffer* output;

    int recv_index;

    int32_t bytes_send;
    int32_t bytes_recv;
};

#endif /* NET_S_H_ */
